<?php
/**
 * Created by PhpStorm.
 * User: Inhere
 * Date: 2015/3/11
 * Time: 21:28
 * Use :
 * File: DataQuery.php
 */

namespace ulue\core\db\base\query;

use ulue\core\db\base\AbstractDbDriver;
use ulue\core\model\DatabaseModel;

abstract class AbstractDataQuery extends AbstractNodeBuilder
{
    /**
     * Property data.
     * @var array
     */
    protected $data;

    /**
     * Property columns.
     * @var array
     */
    protected $columns;

    /**
     * Property values.
     * @var array
     */
    protected $values;

    /**
     * Property dataSet.
     * @var array
     */
    protected $dataSet;

    /**
     * Property dataType. ['array', 'object', 'model']
     * @var string
     */
    protected $dataType = 'array';

    /**
     * Property modelClass.
     * @var DatabaseModel
     */
    protected $modelClass = null;

    const DATA_ARRAY  = 'array';    // a array
    const DATA_OBJECT = 'object';   // a stdClass object
    const DATA_MODEL  = 'model';    // a extends DatabaseModel object

    public function setData(array $data)
    {
        $this->data = $data;

        return $this;
    }

    public function columns(array $columns)
    {
        $this->columns = $columns;

        return $this;
    }

    public function values(array $values)
    {
        $this->values = $values;

        return $this;
    }

    public function insert()
    {
        $sql = $this->buildCreate($this->columns, $this->values);

        return $this->db->exec( $sql );
    }

    public function update()
    {
        $sql = $this->buildUpdate($this->columns, $this->values);

        return $this->db->exec( $sql );
    }

    public function delete()
    {
        $sql = $this->buildDelete();

        return $this->db->exec( $sql );
    }


    public function exists($condition, array $params=[])
    {}

////////////////////////////////////////// find ////////////////////////////////////////////

    /**
     * 调用预准备
     * @return AbstractDbDriver
     */
    public function prepare()
    {
        return $this->db->setSql($this->toString())->prepare();
    }

    /**
     * @return mixed
     */
    public function execute()
    {
        $this->prepare();

        return $this->db->execute($this->params);
    }

    protected $dataToArray = false;

    /**
     * @return array
     */
    public function dataToArray()
    {
        $this->dataToArray = true;

        return $this;
    }

    /**
     * @param string $dataType
     * array -- return array
     * object -- will return a object form \stdClass
     * model -- will return data model object, required setting property $modelClass
     * @throws \DMissArgumentException
     * @return mixed|null
     */
    public function one($dataType='model')
    {
        $dataType = $dataType ?: $this->dataType;

        if ( !in_array($dataType, ['array', 'object', 'model']) ) {
            throw new \DMissArgumentException("第一个参数 \$dataType 仅允许设置 ['array', 'object', 'model'] 中的一个.传入值：$dataType");
        }

        if (!$this->limit) {
            $this->limit();
        }

        $this->execute();

        // object
        if ($dataType == self::DATA_OBJECT) {
            return $this->db->readObject();
        }

        // 没有数据记录
        if ( ! $this->dataSet = $this->db->readOne() ) {
            if (APP_DEBUG) {
                \Trigger::notice('没有找到需要的数据记录, SQL: ' . $this->db->getLastSql() );
            }

            return null;
        }

        // array
        if ($dataType == self::DATA_ARRAY || $this->dataToArray) {
            return $this->dataSet;
        }

        $class = $this->modelClass;
        $model = $class::getNewInstance();

        // model 载入数据到模型
        if (!$model || !($model instanceof DatabaseModel) ) {
            \Trigger::error('请提供数据模型实例来载入数据，且必须继承自 DatabaseModel 。');
        }

        $model->paddingModel($this->dataSet, true);
        $model->afterFind();

        return $model;
    }

    /**
     * @param string $dataType @see self::one()
     * @param null|string $setKey 用记录的某一列的值来设置数据列表的索引键，不设置则是数组默认的数字索引 array(0=>v1,1=>v2,... ...)
     * e.g. 可用 主键id等值唯一的列来设置对应记录的索引键 array(id1=>v1, id2=>v2, ... ...)
     * @throws \DMissArgumentException
     * @return array|null
     */
    public function all($dataType='array', $setKey=null)
    {
        $dataType = $dataType ?: $this->dataType;

        if ( !in_array($dataType, ['array', 'object']) ) { //, 'model'
            throw new \DMissArgumentException("第一个参数 \$dataType 仅允许设置 ['array', 'object'] 中的一个.传入值：$dataType");
        }

        $this->execute();

        if ($setKey) {
            $this->key = $setKey;
        }

        // object
        if ($dataType == self::DATA_OBJECT) {
            $this->dataSet = $this->db->readObjectList($this->key, $class = '\stdClass');
        // array $dataType == self::DATA_ARRAY
        } else {
            $this->dataSet = $this->db->readAssocList($this->key);
        }

        // 不载入数据到模型 -- 请提前调用 self::dataToArray()
        // e.g. XxModel::find()->dataToArray()->all()
        if ( $this->dataToArray ) {
            return $this->dataSet;
        }

        $class = $this->modelClass;
        $model = $class::getNewInstance();

        //model, 载入数据到模型
        if (!$model || !($model instanceof DatabaseModel) )
        {
            \Trigger::error('请提供数据模型实例来载入数据，且必须继承自 DatabaseModel 。');
        }

        return $model->paddingModels($this->dataSet, true, $this->key);
    }

    /**
     * all() alias method
     * @param string $dataType
     * @param null|string $setKey
     * @return array|null
     * @throws \DMissArgumentException
     */
    public function multi($dataType='', $setKey=null)
    {
        return $this->all($dataType, $setKey);
    }

////////////////////////////////////////// count ////////////////////////////////////////////

    public function count()
    {
        $this->select = 'SELECT COUNT(*) AS total';

        return $this->db->setSql($this->toString())->queryOne()['total'];
    }

    // SELECT COUNT( DISTINCT id ) FROM tableName; 计算talbebName表中id不同的记录有多少条
    public function countDistinct($column='id')
    {
        $this->select = "SELECT COUNT(DISTINCT $column) AS total";

        return $this->db->setSql($this->toString())->queryOne()['total'];
    }

    public function countDist($column='id')
    {
        $this->select = "SELECT COUNT(DISTINCT $column) AS total";

        return $this->countDistinct($column);
    }

    // SELECT DISTINCT id FROM tableName; 返回记录不同的id的具体值的时候可以用
    // SELECT id, type, count(DISTINCT id) FROM tableName
    public function distinct($column)
    {
        $this->select = "SELECT DISTINCT $column";

        return $this;
    }

////////////////////////////////////////// attr ////////////////////////////////////////////

    public function setKey($value)
    {
        $this->key = $value;

        return $this;
    }

    /**
     * 设置要载入数据的模型
     * @param string $class
     * @return $this
     */
    public function setModelClass($class)
    {
        $this->modelClass = $class;

        return $this;
    }

    /**
     * @return string
     */
    public function getModelClass()
    {
        return $this->modelClass;
    }

    /**
     * 设置要要获取数据的类型
     * @param string $dataType
     * @return $this
     */
    public function setDataType($dataType)
    {
        $this->dataType = $dataType;

        return $this;
    }

    /**
     * @return string
     */
    public function getDataType()
    {
        return $this->dataType;
    }


}//end class DataQuery